package mythmultibatchparser;

import helpers.SystemInHelper;

import jargs.gnu.CmdLineParser;

import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import parsers.ImdbParser;
import parsers.NameParser;
import parsers.ParserCommand;
import parsers.TVParser;
import datamodel.Entry;
import datamodel.TVShow;

public class MythMultiBatchParser {
	// libmysql-java

	/** The database object for all SQL queries */
	MythDatabase database;

	/** Vector to keep track of which TV shows the user has been queried about */
	Vector<TVShow> tvShows = new Vector<TVShow>();

	/** The various types of parsers available */
	ParserCommand[] parsers = { new ImdbParser(), new TVParser( tvShows ) };

	/** The amount of time to sleep between gets of web pages */
	static final int PARSER_SLEEP = 500;

	Logger logger = Logger.getLogger( "MythMultiBatchParser" );

	/** The user name for the database access */
	String username;

	/** The password for the database access */
	String password;

	/** The ip address for the database access */
	String ip;

	/** The title to a certain show or movie */
	String titleName;

	/** Whether or not to only work with new database entries */
	Boolean all;
	
	/** Whether or not to force a 4:3 ratio for all entries */
	Boolean force43;

	/**
	 * Constructor
	 * 
	 * @param args
	 *            The arguments passed in on the command line
	 */
	public MythMultiBatchParser( String[] args ) {
		parseCommandLineOptions( args );
	}

	/**
	 * Parses the command line arguments
	 * 
	 * @param args
	 *            The arguments passed in on the command line
	 */
	private void parseCommandLineOptions( String[] args ) {
		CmdLineParser parser = new CmdLineParser();

		CmdLineParser.Option usernameOption = parser.addStringOption( 'u', "username" );
		CmdLineParser.Option passwordOption = parser.addStringOption( 'p', "password" );
		CmdLineParser.Option ipOption = parser.addStringOption( 'i', "ip" );
		CmdLineParser.Option titleNameOption = parser.addStringOption( 't', "title" );

		CmdLineParser.Option allOption = parser.addBooleanOption( 'a', "all" );
		CmdLineParser.Option force43Option = parser.addBooleanOption( 'f', "--force43ratio" );
		CmdLineParser.Option helpOption = parser.addBooleanOption( 'h', "help" );

		try {
			parser.parse( args );
		} catch ( CmdLineParser.OptionException e ) {
			System.err.println( e.getMessage() );
			printUsage();
			System.exit( -1 );
		}

		username = (String) parser.getOptionValue( usernameOption, "mythtv" );
		password = (String) parser.getOptionValue( passwordOption, "mythtv" );
		ip = (String) parser.getOptionValue( ipOption, "127.0.0.1" );
		titleName = (String) parser.getOptionValue( titleNameOption );

		all = (Boolean) parser.getOptionValue( allOption, Boolean.FALSE );
		force43 = (Boolean) parser.getOptionValue( force43Option, Boolean.FALSE );

		// See if the user just wants the help output
		if ( parser.getOptionValue( helpOption ) != null ) {
			printUsage();
			System.exit( 0 );
		}
	}

	/**
	 * Prints out the program usage
	 */
	private void printUsage() {
		System.out
				.println( "Usage: MythMultiBatchParser [{-u,--username} dbusername] "
						+ "[{-p,--password} dbpassword ] [{-i,--ip} dbipaddress] [{-t,--title} matchtitle] [-a,--all] [-h,--help]" );
		System.out.println( "Usage Examples:" );
		System.out.println( "Changing database parameters:" );
		System.out.println( "MythMultiBatchParser -u mythtv -p mythtv -i 127.0.0.1" );
		System.out.println( "Specify a title to match:" );
		System.out.println( "MythMultiBatchParser -t Star" );
		System.out.println( "Work on a all database entries (default is to only work with new entries):" );
		System.out.println( "MythMultiBatchParser -a" );
	}

	/**
	 * Executes all of the program's functionality
	 */
	public void execute() {
		database = new MythDatabase( username, password, ip, titleName, all, force43 );

		database.connectToDatabase();

		Vector<Entry> entries = database.getEntries();

		// Run the entries through the name parser to parse out the info inside
		parseNames( entries );

		// Figure out which parser should be used on the entry
		findParser( entries );

		// Search for the entry on the parser
		runSearchParsing( entries );

		// We have hits, now lets ask the user which ones are correct
		queryUserHits( entries );

		// Do the actual parsing on the site
		runFinalParsing( entries );

		// Put everything in the database
		database.updateEntries( entries );

		// Now close the connection and exit
		database.closeConnection();

		System.out.println( "Finished." );
	}

	/**
	 * Runs the final parsing on all of the entries that has not already been
	 * processed
	 * 
	 * @param entries
	 *            All of the entries
	 */
	private void runFinalParsing( Vector<Entry> entries ) {
		for ( Entry entry : entries ) {
			if ( entry.isProcessed() == true ) {
				// This entry is already done
				continue;
			}

			try {
				entry.getParser().executeFinalParsing( entry );

				Thread.sleep( PARSER_SLEEP );
			} catch ( Exception e ) {
				entry.setProcessed( true );

				logger.log( Level.WARNING, "Encountered an error while trying to parse " + entry.getTitle(), e );

				System.out.println( "Encountered an error while trying to parse " + entry.getTitle() );
			}
		}
	}

	/**
	 * Queries the user about all of entries that have not been processed
	 * 
	 * @param entries
	 *            All of the entries
	 */
	private void queryUserHits( Vector<Entry> entries ) {
		for ( Entry entry : entries ) {
			if ( entry.isProcessed() == true ) {
				// This entry is already done
				continue;
			}

			System.out.println( entry.getTitle() + " found:" );

			Set<Map.Entry<String, String>> values = entry.getPossibleMatch().getMatches().entrySet();
			int counter = 1;

			for ( Map.Entry<String, String> tempEntry : values ) {
				System.out.println( counter + ". " + tempEntry.getKey() );
				counter++;
			}

			System.out.println( "Choose a value (0=none): " );

			Integer inputInteger;

			// Keep asking until you get something
			while ( (inputInteger = checkUserInputValue( SystemInHelper.getUserInput(), values )) == null ) {
			}

			if ( inputInteger.intValue() != 0 ) {
				counter = 1;
				for ( Map.Entry<String, String> tempEntry : values ) {
					if ( counter == inputInteger.intValue() ) {
						entry.setInetref( tempEntry.getValue() );
						break;
					}

					counter++;
				}
			} else {
				// The use chose 0 and doesn't want the file processed
				entry.setProcessed( true );
			}
		}
	}

	/**
	 * Runs the parsing that searches for the entries
	 * 
	 * @param entries
	 *            All of the entries
	 */
	private void runSearchParsing( Vector<Entry> entries ) {
		for ( Entry entry : entries ) {
			try {
				entry.getParser().executeSearchParsing( entry );

				Thread.sleep( PARSER_SLEEP );
			} catch ( Exception e ) {
				entry.setProcessed( true );

				logger.log( Level.WARNING, "Could not find a match for entry " + entry.getTitle() + " file "
						+ entry.getFilename(), e );

				System.out.println( "Could not find a match for entry " + entry.getTitle() + " file "
						+ entry.getFilename() );
			}
		}
	}

	/**
	 * Finds the correct parser for the entry
	 * 
	 * @param entries
	 *            All of the entries
	 */
	private void findParser( Vector<Entry> entries ) {
		for ( Entry entry : entries ) {
			for ( ParserCommand parser : parsers ) {
				if ( parser.isParseable( entry ) == true ) {
					entry.setParser( parser );

					break;
				}
			}
		}
	}

	/**
	 * Parse the entry to find the correct name of the TV show or movie
	 * 
	 * @param entries
	 *            All of the entries
	 */
	private void parseNames( Vector<Entry> entries ) {
		NameParser nameParser = new NameParser( tvShows );
		for ( Entry entry : entries ) {
			nameParser.execute( entry );
		}
	}

	/**
	 * Makes sure that the user gave the correct input when queried
	 * 
	 * @param input
	 *            The input from the user
	 * @param values
	 *            The possible values
	 * @return The number that was input by the user or null if it was not
	 *         correct
	 */
	private Integer checkUserInputValue( String input, Set<Map.Entry<String, String>> values ) {
		int inputNumber;

		try {
			inputNumber = Integer.parseInt( input );
		} catch ( Exception e ) {
			System.out.println( "Choose a value (0=none): " );
			return null;
		}

		if ( inputNumber < 0 ) {
			// The number is too small
			System.out.println( "Choose a value (0=none): " );
			return null;
		} else if ( inputNumber > values.size() ) {
			// The number is too big
			System.out.println( "Choose a value (0=none): " );
			return null;
		} else {
			// The number is just right
			return new Integer( input );
		}
	}

	public static void main( String[] args ) {
		MythMultiBatchParser batchParser = new MythMultiBatchParser( args );
		batchParser.execute();
	}
}
